package com.anttribe.components.db.sql.statement.dialect.mysql;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;

import com.anttribe.components.db.sql.statement.dialect.StatementDialect;
import com.anttribe.components.db.sql.statement.fragment.AbstractFragmentSqlStatement;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.text.StringSubstitutor;

import com.anttribe.components.db.sql.exception.SqlStatementException;
import com.anttribe.components.db.sql.format.SqlFormatter;
import com.anttribe.components.db.sql.statement.CreateTableSqlStatement;
import com.anttribe.components.db.metadata.Dialect;
import com.anttribe.components.db.metadata.entity.DbColumn;
import com.anttribe.components.db.metadata.entity.DbIndex;
import com.anttribe.components.db.metadata.entity.PrimaryKey;

import lombok.Getter;
import lombok.Setter;

/**
 * @author zhaoyong
 * @date 2022/11/21 0021
 */
@Getter
@Setter
@StatementDialect(dialect = Dialect.MYSQL)
public class MySqlCreateTableSqlStatement extends CreateTableSqlStatement {

    private static final String VAR_TABLENAME = "tableName";
    private static final String VAR_COLUMNS = "columns";
    private static final String VAR_PRIMARYKEY = "primaryKey";
    private static final String VAR_INDEXS = "indexs";
    private static final String VAR_TABLEOPTIONS = "tableOptions";

    @Override
    public String generateSqlInner() {
        String sqlTemplate = this.buildSqlTemplate();
        StringSubstitutor StringSubstitutor = new StringSubstitutor(this.buildValueMap());
        // 替换变量
        return StringSubstitutor.replace(sqlTemplate);
    }

    /**
     * 构造SqlTemplate
     *
     * @return
     */
    private String buildSqlTemplate() {
        // 创建表
        StringBuilder sqlTemplateStrB = new StringBuilder("CREATE TABLE IF NOT EXISTS").append(" ").append("`${")
            .append(VAR_TABLENAME).append("}`");

        // 定义
        StringBuilder createDefinitionStrB =
            new StringBuilder().append("(").append(formatOptions.isPrettyFormat() ? SqlFormatter.SYMBOL_WRAPPING_LINE : "");
        if (CollectionUtils.isEmpty(dbTable.getColumns())) {
            throw new SqlStatementException("A table must have at least 1 column");
        }

        // 列定义
        createDefinitionStrB.append(formatOptions.isPrettyFormat() ? SqlFormatter.SYMBOL_TAB : "").append("${").append(VAR_COLUMNS)
            .append("}");
        // 主键
        Optional.ofNullable(dbTable.getPrimaryKey()).ifPresent((primaryKey) -> createDefinitionStrB.append(",")
            .append(formatOptions.isPrettyFormat() ? SqlFormatter.SYMBOL_WRAPPING_LINE : "")
            .append(formatOptions.isPrettyFormat() ? SqlFormatter.SYMBOL_TAB : "").append("${").append(VAR_PRIMARYKEY).append("}"));
        // 索引
        if (!CollectionUtils.isEmpty(dbTable.getIndexes())) {
            Optional.ofNullable(dbTable.getIndexes()).ifPresent(indexs -> createDefinitionStrB.append(",")
                .append(formatOptions.isPrettyFormat() ? SqlFormatter.SYMBOL_WRAPPING_LINE : "")
                .append(formatOptions.isPrettyFormat() ? SqlFormatter.SYMBOL_TAB : "").append("${").append(VAR_INDEXS).append("}"));
        }
        createDefinitionStrB.append(formatOptions.isPrettyFormat() ? SqlFormatter.SYMBOL_WRAPPING_LINE : "").append(")");
        sqlTemplateStrB.append(" ").append(createDefinitionStrB);

        // 可选参数
        StringBuilder tableOptionsStrB = new StringBuilder().append("${").append(VAR_TABLEOPTIONS).append("}");
        sqlTemplateStrB.append(" ").append(tableOptionsStrB);

        return sqlTemplateStrB.toString();
    }

    /**
     * 构造ValueMap
     *
     * @return Map<String, Object>
     */
    private Map<String, Object> buildValueMap() {
        Map<String, Object> valueMap = new HashMap<>();
        valueMap.put(VAR_TABLENAME, this.dbTable.getTableName());
        valueMap.put(VAR_COLUMNS, generateColumnsSql());
        valueMap.put(VAR_PRIMARYKEY, generatePrimaryKeySql());
        valueMap.put(VAR_INDEXS, generateIndexsSql());
        valueMap.put(VAR_TABLEOPTIONS, generateTableOptionsSql());
        return valueMap;
    }

    /**
     * 构造数据库列sql
     *
     * @return String
     */
    private String generateColumnsSql() {
        StringBuilder columnsStringB = new StringBuilder("");

        List<String> columnSqls = new ArrayList<>();
        for (DbColumn dbColumn : dbTable.getColumns()) {
            ColumnSqlStatement columnSqlStatement = new ColumnSqlStatement();
            columnSqlStatement.setColumn(dbColumn);
            String columnSql = columnSqlStatement.generateSql();
            columnSqls.add(columnSql);
        }
        columnsStringB.append(StringUtils.join(columnSqls,
            ", " + (formatOptions.isPrettyFormat() ? SqlFormatter.SYMBOL_WRAPPING_LINE + SqlFormatter.SYMBOL_TAB : ""), 0, columnSqls.size()));
        return columnsStringB.toString();
    }

    /**
     * 构造主键sql
     *
     * @return String
     */
    private String generatePrimaryKeySql() {
        StringBuilder primaryKeyStringB = new StringBuilder("");

        PrimaryKey primaryKey = dbTable.getPrimaryKey();
        if (null != primaryKey && !CollectionUtils.isEmpty(primaryKey.getColumns())) {
            primaryKeyStringB.append("PRIMARY KEY");
            primaryKeyStringB.append("(");
            int i = 0;
            for (String column : primaryKey.getColumns()) {
                if (i++ > 0) {
                    primaryKeyStringB.append(", ");
                }
                primaryKeyStringB.append("`").append(column).append("`");
            }
            primaryKeyStringB.append(")");
        }
        return primaryKeyStringB.toString();
    }

    /**
     * 构造索引的dql
     *
     * @return String
     */
    private String generateIndexsSql() {
        if (!CollectionUtils.isEmpty(dbTable.getIndexes())) {
            StringBuilder indexsStringB = new StringBuilder("");

            List<String> indexSqls = new ArrayList<>();
            for (DbIndex dbIndex : dbTable.getIndexes()) {
                if (CollectionUtils.isEmpty(dbIndex.getColumns())) {
                    continue;
                }

                IndexSqlStatement indexSqlStatement = new IndexSqlStatement();
                indexSqlStatement.setDbIndex(dbIndex);
                String indexSql = indexSqlStatement.generateSql();
                indexSqls.add(indexSql);
            }
            indexsStringB.append(StringUtils.join(indexSqls,
                ", " + (formatOptions.isPrettyFormat() ? SqlFormatter.SYMBOL_WRAPPING_LINE + SqlFormatter.SYMBOL_TAB : ""), 0, indexSqls.size()));
            return indexsStringB.toString();
        }
        return "";
    }

    /**
     * 构造表可选信息sql
     *
     * @return String
     */
    private String generateTableOptionsSql() {
        StringBuilder tableOptionsSqlStrB = new StringBuilder("");
        // 默认字符集
        tableOptionsSqlStrB.append("CHARSET").append("=").append("utf8");
        tableOptionsSqlStrB.append(" ");
        // 表注释
        Optional.ofNullable(dbTable.getComment()).ifPresent(
            comment -> tableOptionsSqlStrB.append("COMMENT").append("=").append("'").append(comment).append("'"));
        return tableOptionsSqlStrB.toString();
    }

    /**
     * 数据列的处理
     */
    @Getter
    @Setter
    class ColumnSqlStatement extends AbstractFragmentSqlStatement {

        private static final String VAR_COLUMNNAME = "columnName";
        private static final String VAR_DATATYPE = "dataType";
        private static final String VAR_COLUMN_NULLABLE = "columnNullable";
        private static final String VAR_COMMENT = "comment";

        private static final String SQL_TEMPLATE = "`${columnName}` ${dataType} ${columnNullable} COMMENT '${comment}'";

        /**
         * sql语句模板
         */
        private String sqlTemplate = SQL_TEMPLATE;

        /**
         * 数据库表的列
         */
        private DbColumn column;

        @Override
        public String generateSqlInner() {
            StringSubstitutor StringSubstitutor = new StringSubstitutor(this.buildValueMap());
            // 替换变量
            return StringSubstitutor.replace(sqlTemplate);
        }

        /**
         * 构造ValueMap
         *
         * @return Map<String, Object>
         */
        private Map<String, Object> buildValueMap() {
            Map<String, Object> valueMap = new HashMap<>();
            valueMap.put(VAR_COLUMNNAME, column.getColumnName());
            valueMap.put(VAR_DATATYPE, column.getDataType());
            valueMap.put(VAR_COLUMN_NULLABLE, column.isNullable() ? "" : "NOT NULL");
            valueMap.put(VAR_COMMENT, column.getComment());
            return valueMap;
        }
    }

    @Getter
    @Setter
    class IndexSqlStatement extends AbstractFragmentSqlStatement {

        private static final String VAR_INDEXNAME = "indexName";

        private static final String VAR_INDEXCOLUMNS = "indexColumns";

        private static final String SQL_TEMPLATE = "KEY `${indexName}`(${indexColumns})";

        /**
         * sql语句模板
         */
        private String sqlTemplate = SQL_TEMPLATE;

        /**
         * 数据库表的索引
         */
        private DbIndex dbIndex;

        @Override
        public String generateSqlInner() {
            StringSubstitutor StringSubstitutor = new StringSubstitutor(this.buildValueMap());
            // 替换变量
            return StringSubstitutor.replace(sqlTemplate);
        }

        /**
         * 构造ValueMap
         *
         * @return Map<String, Object>
         */
        private Map<String, Object> buildValueMap() {
            Map<String, Object> valueMap = new HashMap<>();
            valueMap.put(VAR_INDEXNAME, dbIndex.getIndexName());
            valueMap.put(VAR_INDEXCOLUMNS, this.generateIndexColumnsSql());
            return valueMap;
        }

        /**
         * 构造索引列的sql
         *
         * @return String
         */
        private String generateIndexColumnsSql() {
            StringBuilder indexColumnsStringB = new StringBuilder("");
            if (!CollectionUtils.isEmpty(dbIndex.getColumns())) {
                int i = 0;
                for (String column : dbIndex.getColumns()) {
                    if (i++ > 0) {
                        indexColumnsStringB.append(",");
                    }
                    indexColumnsStringB.append("`").append(column).append("`");
                }
            }
            return indexColumnsStringB.toString();
        }

    }

}